package org.xx.armory.rpc.routing;

import org.xx.armory.rpc.RpcVerb;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;

/**
 * 路由表。
 *
 * @author Haart
 * @see Route
 * @see RouteData
 */
public final class RouteTable
        implements List<Route> {
    private final List<Route> innerData;

    /**
     * 构造路由表。
     */
    public RouteTable() {
        innerData = new ArrayList<>();
    }

    /**
     * 通过指定的集合构造路由表。
     *
     * @param c
     *         指定的集合。
     */
    public RouteTable(final Collection<Route> c) {
        innerData = new ArrayList<>();
        if (c != null) {
            innerData.addAll(c);
        }
    }

    /**
     * 从路由表中查找指定的URL能够匹配的路由。
     *
     * @param url
     *         指定的URL, 应当是形如{@literal /abc/def}的绝对URL, 但是不包含域名部分。
     * @param httpMethod
     *         HTTP请求的方法。
     * @return 匹配的路由, 如果找不到则返回{@code null}。
     * @throws NullPointerException
     *         如果参数{@code url}是{@code null}, 或者参数{@code httpMethod}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code url}只包含空白字符。
     */
    public MatchResult find(
            String url,
            RpcVerb httpMethod
    ) {
        url = notBlank(url, "url").trim();
        notNull(httpMethod, "httpMethod");

        for (final Route route : innerData) {
            final RouteData routeData = route.match(url, httpMethod);
            if (routeData != null) {
                return new MatchResult(route, routeData);
            }
        }

        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int size() {
        return this.innerData.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEmpty() {
        return this.innerData.isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean contains(Object o) {
        return this.innerData.contains(o);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterator<Route> iterator() {
        return this.innerData.iterator();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object[] toArray() {
        return this.innerData.toArray();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("SuspiciousToArrayCall")
    public <T> T[] toArray(final T[] a) {
        return this.innerData.toArray(a);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean add(final Route route) {
        return !this.innerData.contains(route) && this.innerData.add(route);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean remove(final Object o) {
        return this.innerData.remove(o);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean containsAll(final Collection<?> c) {
        return this.innerData.containsAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAll(final Collection<? extends Route> c) {
        boolean ret = false;
        for (final Route route : c) {
            if (this.add(route)) {
                ret = true;
            }
        }

        return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAll(
            int index,
            Collection<? extends Route> c
    ) {
        boolean ret = false;
        for (final Route route : c) {
            if (!this.innerData.contains(route)) {
                this.innerData.add(index++, route);
                ret = true;
            }
        }

        return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean removeAll(final Collection<?> c) {
        return this.innerData.removeAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean retainAll(final Collection<?> c) {
        return this.innerData.retainAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        this.innerData.clear();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Route get(int index) {
        return this.innerData.get(index);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Route set(
            int index,
            Route route
    ) {
        return this.innerData.set(index, route);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void add(
            int index,
            Route route
    ) {
        if (!this.innerData.contains(route)) {
            this.innerData.add(index, route);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Route remove(int index) {
        return this.innerData.remove(index);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int indexOf(Object route) {
        return this.innerData.indexOf(route);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int lastIndexOf(Object route) {
        return this.innerData.lastIndexOf(route);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ListIterator<Route> listIterator() {
        return this.innerData.listIterator();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ListIterator<Route> listIterator(int index) {
        return this.innerData.listIterator(index);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Route> subList(
            int fromIndex,
            int toIndex
    ) {
        return this.innerData.subList(fromIndex, toIndex);
    }

    /**
     * 路由匹配结果。
     *
     * @author Haart
     */
    public static final class MatchResult {
        private final Route route;
        private final RouteData routeData;

        /**
         * 构造匹配结果。
         *
         * @param route
         *         匹配的路由。
         * @param routeData
         *         匹配的路由数据。
         * @throws NullPointerException
         *         如果参数{@code route}是{@code null}或者参数{@code routeData}是{@code null}。
         */
        private MatchResult(
                Route route,
                RouteData routeData
        ) {
            notNull(route, "route");
            notNull(routeData, "routeData");

            this.route = route;
            this.routeData = routeData;
        }

        /**
         * 获取匹配的路由。
         *
         * @return 匹配的路由。
         */
        public final Route getRoute() {
            return this.route;
        }

        /**
         * 获取匹配的路由数据。
         *
         * @return 匹配的路由数据。
         */
        public final RouteData getRouteData() {
            return this.routeData;
        }
    }

}
